package cn.iocoder.yudao.module.bk.service.lwbookoutapply;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.bk.controller.admin.lwbook.vo.LwBookRespVO;
import cn.iocoder.yudao.module.bk.controller.admin.lwbookinapply.vo.BkImportExcelVO;
import cn.iocoder.yudao.module.bk.controller.admin.lwbookinapply.vo.BkImportRespVO;
import cn.iocoder.yudao.module.bk.controller.admin.lwbookinapply.vo.InExamineReq;
import cn.iocoder.yudao.module.bk.controller.admin.lwbookoutapply.vo.LwBookOutApplyPageReqVO;
import cn.iocoder.yudao.module.bk.controller.admin.lwbookoutapply.vo.LwBookOutApplySaveReqVO;
import cn.iocoder.yudao.module.bk.controller.app.vo.IdEntReq;
import cn.iocoder.yudao.module.bk.dal.dataobject.lwbook.LwBookDO;
import cn.iocoder.yudao.module.bk.dal.dataobject.lwbookoutapply.LwBookOutApplyDO;
import cn.iocoder.yudao.module.bk.dal.dataobject.publisher.PublisherDO;
import cn.iocoder.yudao.module.bk.dal.enm.BookStoreEnum;
import cn.iocoder.yudao.module.bk.dal.enm.LwProcessEnum;
import cn.iocoder.yudao.module.bk.dal.mysql.lwbook.LwBookMapper;
import cn.iocoder.yudao.module.bk.dal.mysql.lwbookoutapply.LwBookOutApplyMapper;
import cn.iocoder.yudao.module.bk.dal.mysql.publisher.PublisherMapper;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.system.api.sms.SmsSendApi;
import cn.iocoder.yudao.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static cn.hutool.core.date.DatePattern.PURE_DATE_PATTERN;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.bk.enums.ErrorCodeConstants.LW_BOOK_IN_APPLY_NOT_EXISTS;
import static cn.iocoder.yudao.module.bk.enums.ErrorCodeConstants.LW_BOOK_OUT_APPLY_NOT_EXISTS;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.IMPORT_LIST_IS_EMPTY;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS;

/**
 * 书籍飘出 Service 实现类
 *
 * @author qy
 */
@Service
@Slf4j
@Validated
public class LwBookOutApplyServiceImpl implements LwBookOutApplyService {

    @Resource
    private LwBookOutApplyMapper lwBookOutApplyMapper;
    @Resource
    private LwBookMapper lwBookMapper;
    @Resource
    private MemberUserApi memberUserApi;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private SmsSendApi smsSendApi;
    @Resource
    private PublisherMapper publisherMapper;

    @Override
    public Long createLwBookOutApply(LwBookOutApplySaveReqVO createReqVO) {
        // 插入
        LwBookOutApplyDO lwBookOutApply = BeanUtils.toBean(createReqVO, LwBookOutApplyDO.class);
        lwBookOutApplyMapper.insert(lwBookOutApply);
        // 返回
        return lwBookOutApply.getId();
    }

    @Override
    public void updateLwBookOutApply(LwBookOutApplySaveReqVO updateReqVO) {
        // 校验存在
        validateLwBookOutApplyExists(updateReqVO.getId());
        // 更新
        LwBookOutApplyDO updateObj = BeanUtils.toBean(updateReqVO, LwBookOutApplyDO.class);
        lwBookOutApplyMapper.updateById(updateObj);
    }

    @Override
    public void deleteLwBookOutApply(Long id) {
        // 校验存在
        validateLwBookOutApplyExists(id);
        // 删除
        lwBookOutApplyMapper.deleteById(id);
    }

    private void validateLwBookOutApplyExists(Long id) {
        if (lwBookOutApplyMapper.selectById(id) == null) {
            throw exception(LW_BOOK_OUT_APPLY_NOT_EXISTS);
        }
    }

    @Override
    public LwBookOutApplyDO getLwBookOutApply(Long id) {
        return lwBookOutApplyMapper.selectById(id);
    }

    @Override
    public PageResult<LwBookOutApplyDO> getLwBookOutApplyPage(LwBookOutApplyPageReqVO pageReqVO) {
        PageResult<LwBookOutApplyDO> res = lwBookOutApplyMapper.selectPage(pageReqVO);
        if (res.getList().isEmpty()) {
            return new PageResult<>();
        }
        //res.getList()的lwBookIds是逗号分隔的字符串，需要转成List
        List<String> bookIds = new ArrayList<>();
        for (LwBookOutApplyDO lwBookOutApplyDO : res.getList()) {
            bookIds.addAll(Arrays.asList(lwBookOutApplyDO.getLwBookIds().split(",")));
            lwBookOutApplyDO.setLwBookIdList(Arrays.asList(lwBookOutApplyDO.getLwBookIds().split(",")));
        }
        List<LwBookDO> books = lwBookMapper.selectByIds(bookIds);

        List<PublisherDO> publisherDOS = publisherMapper.selectList(new LambdaQueryWrapperX<PublisherDO>()
                .select(PublisherDO::getId, PublisherDO::getName)
                .eq(PublisherDO::getStatus, 1)
        );
        for (LwBookOutApplyDO outApplyRe : res.getList()) {
            outApplyRe.setBooks(books.stream()
                    .filter(book -> outApplyRe.getLwBookIdList().contains(book.getId().toString()))
                    .collect(Collectors.toList()));
            //是否是合规出版社
            for (LwBookDO book : outApplyRe.getBooks()) {
                boolean isCompliance = publisherDOS.stream()
                        .anyMatch(publisherDO -> publisherDO.getName().equals(book.getPublisher()));
                book.setCompliance(isCompliance);
            }
        }
        Set<Long> uids = res.getList().stream().map(LwBookOutApplyDO::getApplyUid).collect(Collectors.toSet());
        Map<Long, MemberUserRespDTO> userMap = memberUserApi.getUserMap(uids);
        for (LwBookOutApplyDO lwBookOutApplyDO : res.getList()) {
            if (userMap.get(lwBookOutApplyDO.getApplyUid()) != null) {
                lwBookOutApplyDO.setApplyName(userMap.get(lwBookOutApplyDO.getApplyUid()).getName());
            } else {
                lwBookOutApplyDO.setApplyName("未知用户");
            }
            if (lwBookOutApplyDO.getInitialReviewUid() != null) {
                AdminUserRespDTO user = adminUserApi.getUser(lwBookOutApplyDO.getInitialReviewUid());
                lwBookOutApplyDO.setInitialReviewName(user.getNickname());
            }
        }
        return res;
    }

    @Override
    public CommonResult<?> outApplyExamine(InExamineReq req) {
        LwBookOutApplyDO lwBookOutApplyDO = lwBookOutApplyMapper.selectById(req.getApplyId());
        if (lwBookOutApplyDO == null) {
            throw exception(LW_BOOK_IN_APPLY_NOT_EXISTS);
        }
        if (!Objects.equals(lwBookOutApplyDO.getProcessStatus(), LwProcessEnum.FIRST_EXAMINE.getProcess())) {
            throw exception(LW_BOOK_IN_APPLY_NOT_EXISTS);
        }
        lwBookOutApplyDO.setProcessStatus(req.getAudit() == 0 ? LwProcessEnum.PASS.getProcess() :
                LwProcessEnum.REJECT.getProcess());
        lwBookOutApplyDO.setInitialReviewDes(req.getDes());
        lwBookOutApplyDO.setInitialReviewUid(getLoginUserId());
        lwBookOutApplyMapper.updateById(lwBookOutApplyDO);
        //审核驳回，则书籍更新为已入库,清除持有者id:因为提交时已经标记为已飘出，
        if (req.getAudit() != 0) {
            List<String> bookIds = Arrays.asList(lwBookOutApplyDO.getLwBookIds().split(","));
            lwBookMapper.update(new LambdaUpdateWrapper<LwBookDO>()
                    .set(LwBookDO::getStoreStatus, BookStoreEnum.IN_STORE.getProcess())
                    .set(LwBookDO::getNowHolder, null)
                    .in(LwBookDO::getId, bookIds)
            );
            //驳回短信
            sendRejectSms(lwBookOutApplyDO);
        }
        return CommonResult.success();
    }

    /**
     * 发送驳回短信
     *
     * @param applyDO 申请
     */
    private void sendRejectSms(LwBookOutApplyDO applyDO) {
        //发送驳回短信 user-apply-reject
        SmsSendSingleToUserReqDTO reqDTO = new SmsSendSingleToUserReqDTO();
        reqDTO.setUserId(applyDO.getApplyUid());
        reqDTO.setTemplateCode("user-apply-reject");
        Map<String, Object> templateParams = new HashMap<>(2);
        templateParams.put("time", LocalDateTimeUtil.format(applyDO.getCreateTime(), "yyyy-MM-dd"));
        reqDTO.setTemplateParams(templateParams);
        try {
            smsSendApi.sendSingleSmsToMember(reqDTO);
        } catch (Exception e) {
            log.error("[sendRejectSms][发送短信异常: {}]", e.getMessage(), e);
        }
    }


    @Override
    public CommonResult<List<LwBookRespVO>> applyBooks(IdEntReq req) {
        LwBookOutApplyDO lwBookOutApplyDO = lwBookOutApplyMapper.selectById(req.getId());
        if (lwBookOutApplyDO == null) {
            throw exception(LW_BOOK_OUT_APPLY_NOT_EXISTS);
        }
        List<LwBookDO> lwBookDOS = lwBookMapper.selectByIds(Arrays.asList(lwBookOutApplyDO.getLwBookIds().split(",")));
        List<PublisherDO> publisherDOS = publisherMapper.selectList(new LambdaQueryWrapperX<PublisherDO>()
                .select(PublisherDO::getId, PublisherDO::getName)
                .eq(PublisherDO::getStatus, 1)
        );
        for (LwBookDO lwBookDO : lwBookDOS) {
            //是否是合规出版社
            boolean isCompliance = publisherDOS.stream()
                    .anyMatch(publisherDO -> publisherDO.getName().equals(lwBookDO.getPublisher()));
            lwBookDO.setCompliance(isCompliance);
        }
        return CommonResult.success(lwBookDOS.stream()
                .map(book -> BeanUtils.toBean(book, LwBookRespVO.class)).collect(Collectors.toList()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BkImportRespVO importBkList(List<BkImportExcelVO> importBks, boolean isUpdateSupport) {
        // 1.1 参数校验
        if (CollUtil.isEmpty(importBks)) {
            throw exception(IMPORT_LIST_IS_EMPTY);
        }
        // 2. 遍历，逐个创建
        BkImportRespVO respVO = BkImportRespVO.builder().createBkNames(new ArrayList<>())
                .updateBkNames(new ArrayList<>()).failureBkNames(new LinkedHashMap<>()).build();
        importBks.forEach(importItem -> {
            try {
                if (!"漂出".equals(importItem.getStatus())) {
                    throw exception("接口仅支持漂出");
                }
                if (StrUtil.isEmpty(importItem.getUsername()) || StrUtil.isEmpty(importItem.getIdCard())
                        || StrUtil.isEmpty(importItem.getIsbn()) || importItem.getSubmitTime() == null) {
                    throw exception("单行数据不全");
                }
                MemberUserRespDTO memberUser = memberUserApi.getUserByIdCard(importItem.getIdCard());
                if (memberUser == null) {
                    throw exception(USER_NOT_EXISTS);
                }
                LambdaQueryWrapperX<LwBookDO> bookQw = new LambdaQueryWrapperX<LwBookDO>()
                        .eq(LwBookDO::getIsbn, importItem.getIsbn())
                        .eq(LwBookDO::getTitle, importItem.getBookName())
                        .eq(LwBookDO::getStoreStatus, BookStoreEnum.OUT_STORE.getProcess())
                        .eq(LwBookDO::getNowHolder, memberUser.getId())
                        .orderByDesc(LwBookDO::getId)
                        .last("limit 1");
                LwBookDO lwBookDO = lwBookMapper.selectOne(bookQw);
                if (lwBookDO == null) {
                    lwBookMapper.insert(new LwBookDO(memberUser.getId(), importItem));
                    lwBookDO = lwBookMapper.selectOne(new LambdaQueryWrapperX<LwBookDO>()
                            .eq(LwBookDO::getIsbn, importItem.getIsbn())
                            .eq(LwBookDO::getTitle, importItem.getBookName())
                            .eq(LwBookDO::getStoreStatus, BookStoreEnum.IN_STORE.getProcess())
                            .orderByDesc(LwBookDO::getId)
                            .last("limit 1"));
                    lwBookDO.setDonor(null);
                }
                Long count = lwBookOutApplyMapper.selectCount(new LambdaUpdateWrapper<LwBookOutApplyDO>()
                        .eq(LwBookOutApplyDO::getLwBookIds, lwBookDO.getId())
                        .eq(LwBookOutApplyDO::getApplyUid, memberUser.getId())
                        .eq(LwBookOutApplyDO::getCreateTime, importItem.getSubmitTime()));
                if (count > 0) {
                    throw exception("数据已存在");
                }
                LwBookOutApplyDO bookOutApply = new LwBookOutApplyDO();
                bookOutApply.setCreateTime(importItem.getSubmitTime());
                bookOutApply.setLwBookIds(lwBookDO.getId().toString());
                bookOutApply.setApplyUid(memberUser.getId());
                bookOutApply.setProcessStatus(LwProcessEnum.PASS.getProcess());
                bookOutApply.setRemark(DateUtil.format(LocalDateTime.now(), PURE_DATE_PATTERN) + " : 系统导入");
                lwBookOutApplyMapper.insert(bookOutApply);
                //更新书
                lwBookDO.setStoreStatus(BookStoreEnum.OUT_STORE.getProcess());
                lwBookDO.setNowHolder(memberUser.getId());
                lwBookMapper.updateById(lwBookDO);
                respVO.getCreateBkNames().add(importItem.getUsername());
            } catch (ServiceException ex) {
                respVO.getFailureBkNames().put(importItem.getBookName() + " : " + importItem.getIsbn(), ex.getMessage());
            }
        });
        return respVO;
    }
}
